home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Scene 96
/
Scene 96 International Edition (Zyklop Software) (Disc 2) (1997).iso
/
misc
/
coding
/
vgacodng
/
part02.txt
< prev
next >
Wrap
Text File
|
1996-08-07
|
11KB
|
224 lines
VGA-Kurs - Part #2
Moin! Hier, nun und jetzt, wie zu erwarten "T.C.P.s' Beginner's Guide To VGA
Coding"(TM), Part II!
Wie schon im ersten Teil gesagt, werde ich diese Serie nur fortsetzen, wenn
sich auch genug Leute melden, mich mit Lob besudeln, mit Klagen zuballern
oder mir einfach ein paar Tips geben.
Na dann, aufi gaits Buam!
Heute wollen wir uns einem wichtigen und für Anfänger oft sehr verwirrenden
Kapitel der VGA-Programmierung zuwenden: Der VGA-Palette!
Aber erst dies: Ihr erinnert euch doch bestimmt an unsere "optimierte"
PutPixel-Routine aus der ersten Ausgabe; Sie schrieb die Pixelwerte direkt
in den Bildschirmspeicher und war so bedeutend schneller als die
Bios-Variante. Wollen mal sehen, ob wir nicht noch ein paar Clocks
rausquetschen können:
procedure PutPixel(x,y:integer;
col:byte);assembler;
asm
mov ax,0A000h
mov es,ax
mov bx,[x]
mov dx,[y]
mov di,bx
mov bx,dx
shl dx,8
shl bx,6
add dx,bx
add di,dx
mov al,[col]
stosb
end;
(Hinweis: Für diese Prozedur müßt ihr entweder im Compiler-Optionsmenü die
286er-Code Erzeugung aktivieren (oder am Anfang des Programms den
Compiler-Schalter {$G+} setzen) oder statt "shl dx,8" 8 mal "shl dx" und
statt "shl bx,6" 6 mal "shl bx" schreiben.)
Um den Sinn dieser Prozedur auf Anhieb zu verstehen, muß man schon etwas mehr
Erfahrung haben: Als erstes wird (wie wahrscheinlich noch alle erkannt haben)
die Adresse des VGA-Segments nach ES geMOVt (Manche werden wissen, daß das
Register-Paar ES:DI einen Zeiger darstellt, und nun in DI das Offset des
gewünschten Pixels stehen muß). Anschließend kommt die Adresse von x bzw. y
nach BX bzw. DX. Nun wird (nachdem x nach BX nach DI und DX nach BX verschoben
wurde, der Inhalt von DX (y) nun also in BX und DX ist) der Wert in DX um 8
(entspricht einer Multiplikation mit 256) und der in BX um 6 (entspricht
Mult. mit 64) nach links geshiftet. Dann werden die beiden addiert, und was
haben wir jetzt? Y mult. mit 320! (Teuflisch, nicht?) Dazu wird nun der Wert
von X addiert, dann die Farbe nach AL gebracht und STOSB aufgerufen. Schon
haben wir den Pixel dort, wo wir ihn haben wollten!
So, nun zu den Paletten. Wie wohl jeder weiß, hat man im Modus 13h 256 Farben
zur Verfügung. Auswählen kann man diese 256 Farben aber aus einem Bereich von
262144 verschiedenen Farbtönen. Nun, die Standard-Auswahl ("Palette") der
VGA-Farben ist nicht sehr klug gewählt und oft findet man die Farbe, die man
braucht, nicht in dieser Auswahl. Also, wie beschafft man sich neue?
Vorerst einmal ein paar Grundlagen (Ich weiß, Theorie ist Bullshit, aber sie
muß trotzdem sein). Ihr habt bestimmt in der Schule mal gelernt, daß Farbtöne
aus den drei Grundfarben entstehen: Rot, Grün (eigentlich Gelb) und Blau
(Wenn nicht, wißt ihr es spätestens jetzt). Dadurch lassen sich theoretisch
endlos viele Farben herstellen (die aber eigentlich niemand braucht ;-),
indem man ebendiese Farben zu bestimmten Anteilen miteinander vermischt.
Aber was hat das jetzt mit der VGA-Palette zu tun? Ganz einfach, sie basiert
auf demselben Prinzip! Wenn man weiß wie, kann man sich über 260000 neue
Farbtöne erstellen! Aber wie? Jede unserer 256 Standard-VGA-Farben besteht
schon aus diesen 3 Farbwerten. Die Farbe 0 z.B. ist schwarz und besteht somit
aus den Werten (0,0,0), also: 0 Rot, 0 Grün, 0 Blau. Farbe 31 dagegen (weiß)
hat die Werte (63,63,63), also die Maximalwerte jedes Farbtons. So sind nun
sämtliche Farben aufgebaut. Wenn man ein tiefes Blau hat (0,0,63), kann man
es aufhellen, indem man die Werte für Rot und Grün heraufsetzt (32,32,63).
Erhöht man dagegen nur den Wert für Rot, erhält man ein dunkleres Blau bis
Lila.
So, nun zum praktischen Teil: Wie setze ich die Palettenwerte einer Farbe?
procedure SetPal(col,R,G,B:byte);
begin
port[$3C8] := col;
port[$3C9] := R;
port[$3C9] := G;
port[$3C9] := B;
end;
Hier wird erst dem DAC-Port(3C8h) die Nummer der Farbe zugewiesen, um ihn auf
einen Schreibzugriff vorzubereiten. Anschließend werden die 3 Farbwerte an
den Port 3C9h abgeschickt, und -zack!- sitzen die neuen Werte in der Palette.
Dies ist sehr nützlich, weil alle Farben der Nummer col auf einen Schlag eine
neue Farbe bekommen, was man trickreich ausnutzen kann. Ein Beißstiel: Die
Palettenmanipulation wird oft in Adventures eingesetzt, wenn in einer Location
ein Wasserfall simuliert werden soll. Dieser wird erst gezeichnet, und
anschließend werden die Werte der Farben des Wasserfalls (meist Blautöne)
ständig miteinander verstauscht, so daß es aussieht, als würde wirklich Wasser
fließen. Aufwendige Animationen, die Speicherplatz verschwenden, bleiben einem
erspart (Diese Methode nennt sich übrigens Colorcycling). Es gibt noch mehr
Anwendungsbeispiele, von denen wir einige besprechen werden.
Allerdings hat diese Pal-Manipulation einen Nachteil:
Es entsteht ein Flackern, was nun wirklich unschön ist. Vermieden werden kann
es, indem man vorher diese Prozedur callt:
procedure WaitRetrace;assembler;
asm
mov dx,3DAh
@x: in al,dx
test al,08h
jnz @x
@y: in al,dx
test al,08h
jz @y
end;
Folgendes: Der Bildschirm wird alle soundsoviele Millisekunden wieder neu
aufgebaut: "Retraced". Wird nun während eines solchen Retrace das Bild
irgendwie verändert, so entsteht ein störendes Flackern. Dies kann vermieden
werden, indem man vor JEDER wichtigen Bildschirmänderung (Sprite zeichnen,
Palette verändern, etc.) den Retrace abwartet. Die Prozedur "weckt" das Input
Status Register 1 der VGA und sieht nach, wie das Bit 3 aussieht. Es zeigt
nämlich an, wie es um den Retrace steht. Die Prozedur wird solange geloopt,
bis der Retrace unten am Bildschirm angelangt ist, dann beendet, worauf man
die Palette verändern kann. Alles klar?
Ach übrigens: Man kann die Palette auch auslesen:
procedure GetPal(col:byte;
var R,G,B:byte);
begin
port[$3C7] := col;
R := port[$3C9];
G := port[$3C9];
B := port[$3C9];
end;
Mit dieser Prozedur kann man die Farbwerte der Farbe col ermitteln.
So, war da nicht die Rede von anderen Anwendungsgebieten der Pal-Mod.?
Hier etwas sehr trickreiches: Wenn man nicht will, daß der Anwender sieht,
was man mit der VGA anstellt, kann man das auch "unsichtbar" machen. Man setzt
einfach sämtliche Farben auf Schwarz:
procedure BlackPal;
var n : byte;
begin
WaitRetrace;
for n := 0 to 255 do
SetPal(n,0,0,0);
end;
Allerdings sollte man vorher seine Palettenwerte sichern, sonst kann man das
Bild schlecht wiederherstellen. Dazu deklarieren wir eine globale Variable
Pal : array[0..255,1..3] of byte. Das Sichern der Palette:
procedure GrabPal;
var n : byte;
begin
for n := 0 to 255 do
GetPal(n,Pal[n,1],Pal[n,2],
Pal[n,3]);
end;
Dies schreibt sämtliche Palettenwerte in das Array Pal. Wollen wir nun alle
Werte wieder herstellen, brauchen wir eine neue Prozedur namens RestorePal,
für die ihr einfach das GetPal in ein SetPal verwandelt und nach dem "begin"
ein "waitretrace" einfügt.
Nachdem man die Palette mit GrabPal gerettet, den Bildschirm mittels BlackPal
abgedunkelt und das Bild (was auch immer das sein mag) auf den VGA
"geklatscht" hat, kann man auch eine andere, effektvollere Art des
Einblendens wählen, als einfach die Palette wiederherzustellen: Fading!
Fading ist ein Paletteneffekt, den man sehr oft zu sehen bekommt. Er
funktioniert folgendermaßen: Nachdem man die oben beschriebenen Schritte
durchgeführt hat und der Bildschirm schwarz ist, schreibt man die gewünschte
Zielpalette in die Variable Pal und erhöht nun solange schrittweise die
Palettenwerte, bis die Zielwerte in Pal erreicht sind. Das geht so:
procedure FadeUp;
var n1,n2 : byte;
Tmp : array[1..3] of byte;
begin
for n1 := 1 to 64 do begin
WaitRetrace;
For n2 := 0 to 255 do begin
GetPal(n2,Tmp[1],Tmp[2],Tmp[3]);
if Tmp[1] < Pal[n2,1] then
inc (Tmp[1]);
if Tmp[2] < Pal[n2,2] then
inc (Tmp[2]);
if Tmp[3] < Pal[n2,3] then
inc (Tmp[3]);
SetPal(n2,Tmp[1],Tmp[2],Tmp[3]);
end;
end;
end;
Bei dieser Prozedur wird zunächst der Retrace abgewartet. Anschließend wird
für jede Farbe überprüft, ob ihre Farbwerte schon denen in der Zielpalette
entsprechen. Wenn nicht, wird der Wert um 1 erhöht. Danach wird der neue
Wert in die Palette eingetragen. Das Ganze wird 64 mal wiederholt.
Umgekehrt geht auch. Will man also ein Bild auf dem VGA langsam ausfaden,
schreibt man sich eine Prozedur wie oben, nur ersetzt man die INCs durch
DECs, die Kleiner-als- durch Größer-als-Zeichen, und die "Pal[n2,?]" durch 0.
So, nun können wir schon ein paar sehr schöne Effekte erzielen, vielleicht
könnt ihr aus den vorgegebenen Routinen noch mehr machen (z.B ein Fading ins
Weiße erzeugt den Eindruck einer Explosion). Ich hoffe ich werde in dem PCH,
in dem diese Anzi erscheint schon eine Menge Feedback lesen.
Nächstes Thema ist höchstwahrscheinlich...Scrollinx!!!
Na dann, bis zur nächsten Ausgabe.
[ This text copyright (c) 1995-96 Johannes Spohr. All rights reserved. ]
[ Distributed exclusively through PC-Heimwerker, Verlag Thomas Eberle. ]
[ ]
[ No part of this document may be reproduced, transmitted, ]
[ transcribed, stored in a retrieval system, or translated into any ]
[ human or computer language, in any form or by any means; electronic, ]
[ mechanical, magnetic, optical, chemical, manual or otherwise, ]
[ without the expressed written permission of the author. ]
[ ]
[ The information contained in this text is believed to be correct. ]
[ The text is subject to change without notice and does not represent ]
[ a commitment on the part of the author. ]
[ The author does not make a warranty of any kind with regard to this ]
[ material, including, but not limited to, the implied warranties of ]
[ merchantability and fitness for a particular purpose. The author ]
[ shall not be liable for errors contained herein or for incidental or ]
[ consequential damages in connection with the furnishing, performance ]
[ or use of this material. ]